{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c8c6c8bb",
   "metadata": {
    "toc": true
   },
   "source": [
    "<h1>Table of Contents<span class=\"tocSkip\"></span></h1>\n",
    "<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#Chapter-7---Continuous-time-averaging-systems\" data-toc-modified-id=\"Chapter-7---Continuous-time-averaging-systems-1\">Chapter 7 - Continuous-time averaging systems</a></span><ul class=\"toc-item\"><li><span><a href=\"#7.1-Example-Systems\" data-toc-modified-id=\"7.1-Example-Systems-1.1\">7.1 Example Systems</a></span><ul class=\"toc-item\"><li><span><a href=\"#7.1.1-Example-#1:-Continuous-time-opinion-dynamics\" data-toc-modified-id=\"7.1.1-Example-#1:-Continuous-time-opinion-dynamics-1.1.1\">7.1.1 Example #1: Continuous-time opinion dynamics</a></span></li><li><span><a href=\"#7.1.3-Example-#3:-Discretization-of-partial-differential-equations\" data-toc-modified-id=\"7.1.3-Example-#3:-Discretization-of-partial-differential-equations-1.1.2\">7.1.3 Example #3: Discretization of partial differential equations</a></span></li></ul></li></ul></li></ul></div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "50ac1eb2",
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib widget\n",
    "\n",
    "# Import packages\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import networkx as nx\n",
    "\n",
    "# For interactive graphs\n",
    "import ipywidgets as widgets\n",
    "\n",
    "# Import self defined functions\n",
    "import lib  # General library\n",
    "\n",
    "# Settings\n",
    "custom_figsize= (6, 4) # Might need to change this value to fit the figures to your screen\n",
    "custom_figsize_square = (5, 5) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e444bc32",
   "metadata": {},
   "source": [
    "# Chapter 7 - Continuous-time averaging systems\n",
    "\n",
    "These Jupyter Notebook scripts contain some examples, visualization and supplements accompanying the book \"Lectures on Network Systems\" by Francesco Bullo http://motion.me.ucsb.edu/book-lns/. These scripts are published with the MIT license. **Make sure to run the first cell above to import all necessary packages and functions and adapt settings in case.** In this script it is necessary to execute cell by cell chronologically due to reocurring examples (Tip: Use the shortcut Shift+Enter to execute each cell). Most of the functions are kept in separate files to keep this script neat."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2bc39308",
   "metadata": {},
   "source": [
    "## 7.1 Example Systems\n",
    "\n",
    "In this section we repeat some example covered in the book for visualization and experimentation. Since we use continuous time models, the simulation must be done in some way of discretized manner. These are shown below.\n",
    "\n",
    "### 7.1.1 Example #1: Continuous-time opinion dynamics\n",
    "\n",
    "For a simulating example of a French-Harary-DeGroot discrete-time averaging model to a Abelson's continuous timeopinion dynamics model, we actually take a look at **Exercise 5.1** and simulate the results here. The simulation of the continuous-time model is done with $x(t+\\tau) = x(t) + -\\bar{L} x(t) \\tau$.\n",
    "\n",
    "First we define our network, plot the graph and as for Exercise 5.1 (i), we draw the condensation graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e4a30124",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Adjacency Matrix\n",
    "A_711 = np.array([\n",
    "    [0.15, 0.15, 0.1, 0.2, 0.4],\n",
    "    [0, 0.55, 0, 0, 0.45],\n",
    "    [0.3, 0.05, 0.05, 0, 0.6],\n",
    "    [0, 0.4, 0.1, 0.5, 0],\n",
    "    [0, 0.3, 0, 0, 0.7]\n",
    "])\n",
    "\n",
    "# Giving pre defined positions away\n",
    "pos711 = {0:[0.1,0.2],1:[.4,.5],2:[.5,.2],3:[.8,.5], 4:[.9,.2]}\n",
    "\n",
    "G_711 = lib.create_G_from_adj_matrix(A_711)\n",
    "\n",
    "fig, axs711 = plt.subplots(1, 3, figsize=(custom_figsize[0]*1.2, custom_figsize[1]))\n",
    "\n",
    "lib.plot_condensated_graph(G_711, axs711, pos711)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "817ad2a1",
   "metadata": {},
   "source": [
    "The next step is to actually simulate the system and plot the results. We can play with the simulation parameters and realize, that as expected the same result as for discretized and continuous system are achieved."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a9f564c5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Settings, can change these\n",
    "tau = 0.2\n",
    "t_total = 200\n",
    "\n",
    "x_init = np.diag(A_711)  # Initial condition\n",
    "\n",
    "# Laplace\n",
    "L_711 = np.diag(np.sum(A_711, 1)) - A_711\n",
    "\n",
    "# Simulation\n",
    "states = np.zeros((t_total,L_711.shape[0]))\n",
    "states[0,:]=x_init\n",
    "for i in range(1,t_total):\n",
    "    states[i,:] = states[i-1,:] + -L_711 @ states[i-1,:] * tau\n",
    "\n",
    "# From here, interactive network representation\n",
    "fig, ax711 = plt.subplots(figsize=custom_figsize)\n",
    "fig, v_bound, pos = lib.init_network_sim_plot(G_711, states, fig, pos=pos711)\n",
    "\n",
    "def inter(timestep):\n",
    "    lib.update_network(timestep['new'], G=G_711, states_m=states, ax=ax711, vbound=v_bound, pos=pos711)\n",
    "    return None\n",
    "\n",
    "# Plot initial configuration\n",
    "lib.update_network(0, G=G_711, states_m=states, ax=ax711, vbound=v_bound, pos=pos711)\n",
    "\n",
    "\n",
    "# Widget\n",
    "# If this cell is executed twice we are making sure in the following, that the previous widget instances are all closed\n",
    "try:\n",
    "    [c.close() for c in widget711.children]  # Note: close_all() does also affect plot, thus list compr.\n",
    "except NameError:  # Only want to except not defined variable error\n",
    "    pass\n",
    "\n",
    "widget711 = lib.create_widgets_play_slider(fnc=inter, minv=0, maxv=t_total-1, step=1, play_speed=1000)\n",
    "\n",
    "display(widget711)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c375c67f",
   "metadata": {},
   "source": [
    "### 7.1.3 Example #3: Discretization of partial differential equations\n",
    "\n",
    "For some intuition and exploration, the heat equation presented in the book is presented below in a minimum example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b260dc41",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Settings for grid\n",
    "n = 4\n",
    "\n",
    "# Simulation Parameter can change these\n",
    "tau2 = 0.01\n",
    "t_total2 = 200\n",
    "c = 0.1  # thermal diffusivity\n",
    "h = 0.2  # Grid length\n",
    "\n",
    "# Initial\n",
    "x_init2 = np.random.rand(n**2) * 100\n",
    "\n",
    "# Two dimensional grid graph\n",
    "G_grid = nx.generators.lattice.grid_graph([n, n])\n",
    "# Save Positions for plot later and normalize coordinates from original node coordinate\n",
    "pos712 = dict(zip(range(0, n**2), [tuple([y/n for y in x]) for x in G_grid.nodes]))\n",
    "\n",
    "# Relabel for simplicity\n",
    "G_grid = nx.relabel.relabel_nodes(G_grid, dict(zip(G_grid.nodes, range(0, n**2))))\n",
    "\n",
    "A_grid = nx.linalg.graphmatrix.adjacency_matrix(G_grid, nodelist=G_grid.nodes).toarray()\n",
    "L_grid = np.diag(np.sum(A_grid, 1)) - A_grid\n",
    "\n",
    "# Simulation\n",
    "states2 = np.zeros((t_total2,L_grid.shape[0]))\n",
    "states2[0,:] = x_init2\n",
    "for i in range(1,t_total2):\n",
    "    states2[i,:] = states2[i-1,:] + -(c/h**2)*L_grid @ states2[i-1,:] * tau2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e86a949f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# From here, interactive network representation\n",
    "fig, ax712 = plt.subplots(figsize=custom_figsize)\n",
    "fig, v_bound, pos712 = lib.init_network_sim_plot(G_grid, states2, fig, pos=pos712)\n",
    "\n",
    "def inter2(timestep):\n",
    "    lib.update_network(timestep['new'], G=G_grid, states_m=states2, ax=ax712, vbound=v_bound, pos=pos712)\n",
    "    return None\n",
    "\n",
    "# Plot initial configuration\n",
    "lib.update_network(0, G=G_grid, states_m=states2, ax=ax712, vbound=v_bound, pos=pos712)\n",
    "\n",
    "\n",
    "# Widget\n",
    "# If this cell is executed twice we are making sure in the following, that the previous widget instances are all closed\n",
    "try:\n",
    "    [c.close() for c in widget712.children]  # Note: close_all() does also affect plot, thus list compr.\n",
    "except NameError:  # Only want to except not defined variable error\n",
    "    pass\n",
    "\n",
    "widget712 = lib.create_widgets_play_slider(fnc=inter2, minv=0, maxv=t_total2-1, step=1, play_speed=1000)\n",
    "\n",
    "display(widget712)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "28b5793c",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.11"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": true,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
